<?php
namespace Core;

class ParseMsg
{
    public $after = '';

    private $_map = array(
        '2002' => 'setHeartSpace',
        '2003' => 'getHeartSpace',
        '2004' => 'setPalpitationInterval',
        '2005' => 'setDomainPort',
        '2006' => 'getDomainPort',
        '2007' => 'setIpPort',
        '2008' => 'getIpPort',
        '2009' => 'setServerPolicy',
        '200a' => 'getServerPolicy',
        '200b' => 'setDevOfflineTime',
        '200c' => 'setWorkMode',
        '200d' => 'setFitler',
        '200e' => 'getFitler',
        '1013' => 'upgradeResult',
        '1011' => 'setFirmwareData',
        '1012' => 'getFirmwareData',
        '4000' => 'setDataSpace',
        '4001' => 'getDataSpace',
        '4002' => 'setDataUploadWay',
        '4003' => 'getDataUploadWay',
        '5103' => 'getUpError',
        '6000' => 'getDevice',
        '6001' => 'getDeviceStatus',
        '6002' => 'getDeviceLocal',
        '6003' => 'getDeviceFault',
        'a000' => 'uploadApData',
        'a001' => 'uploadStaData',
        'a002' => 'uploadRfData'
    );


    //错误消息
    protected static $errMsg = array(
        'cfg' => array(
            '上次校验数据错误',
            '上次命令错误',
            '上次执行失败',
            '服务器响应超时',
            '通讯异常'
        ),
        'data' => array(
            '上次校验数据错误',
            '上次命令错误',
            '上次执行失败',
            '服务器响应超时',
            '通讯异常',
            '连接服务器失败',
        ),
        'device' => array(
            'RF模块错误',
            'WiFi模块故障',
            'BT模块故障',
            'GPRS模块故障',
            '网卡模块故障',
            '未知故障',
        ),
        'power' => array(
            '电压跳动过大',
            '电量不足随时关机',
        ),

        'temperature' => array(
            '温度急剧上升',
            '温度急剧下降',
            '温度过高',
            '温度过低',
        )
    );


    protected static $devStatus = array(
        'net' => array(
            0 => '未知',
            1 => 'wifi',
            2 => '网卡',
            3 => 'GPRS',
            4 => '3G',
            5 => '4G',
            255 => '其他',
        ),
        'fan'=> array(
            0 => '不存在',
            1 => '关闭',
            2 => '低速运行',
            3 => '中速运行',
            4 => '高速运行',
            5 => '全速运行',
        ),
        'temp' => array(
            0 => '未知',
            127 => '0',
            126  => '-1',
            128 => '1',
        ),
        'battery' => array(
            'status' => array(
                0 => '未知',
                1 => '供电中',
                2 => '充电中',
                3 => '正常',
            ),
            'quantity' => array(
                0 => '未知',
                1 => '100%',
                2 => '80%',
                3 => '60%',
                4 => '40%',
                5 => '20%',
                6 => '随时关机',
                7 => '未知',
            ),
        ),
    );


    public function parsein($string='', $token)
    {
        /*60 00 00 21 31 23 35 f9 54 2d 36 1e 39 2b 3a 20 3d 17 3e 24 21 12 0a 01 24 98 5b c3 9e 1b 3e dd a6 */
        /* 1502695187 */
        $hexStr = $string;//bin2hex($string);
        $hexType = substr($hexStr, 0, 4);
        $hexMsgLen = substr($hexStr, 4, 4);
        $typeArray = array("a000", "a001", "a002");
        $upgradeType = array("1011", "1012", "1013");
        if( in_array($hexType, $typeArray)) {
            $xorBody = bin2hex(xor_code(pack('H*', substr($hexStr, 8)), pack('H*', dechex($token))));
            $xorString = $hexType . $hexMsgLen . $xorBody;
            if (!$xorBody) return;
            $data = $this->parse($xorString, true, false);
        } elseif ($hexType == '2000') {
            //心跳暂不做处理
            return;
        } elseif ( in_array($hexType, $upgradeType)) {
            $data['type'] = $hexType;
            $data['body'] = substr($hexStr, 8, -2);
            $data['veriyf'] = substr($hexStr, -2, 2);
            //检查校验和
        } else {
            if (false == $md5type = config('server')['md5type']) {
                $md5type = 64;
            }
            $string = bin2hex(decode_data($string, $token, $md5type));   //解密
            $this->after = substr($string,2);
            $data = $this->parse(substr($string,2));
        }

        $msgType = $data['type'];

        return $this->getdata($data['body'], $msgType);



    }

    /**
     * 设置心跳间隔
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 2002
     * @author qmx
     */
    public function setHeartSpace($body,$msgType){
        return $this->handStatus($body,$msgType);
    }

    /**
     * 获取心跳间隔
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 2003
     * @author qmx
     */
    public function getHeartSpace($body,$msgType){
        return $this->handStatus($body,$msgType);
    }

    /**
     * 设置设备重启或者复位
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 2004
     * @author wanyujun
     */
    public function setPalpitationInterval($body,$msgType)
    {
        return $this->handStatus($body,$msgType);
    }

    /**
     * 设置服务器的域名和端口
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 2005
     * @author wanyujun
     */
    public function setDomainPort($body,$msgType)
    {
        return $this->handStatus($body,$msgType);
    }

    /**
     * 获取服务器的域名和端口
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 2006
     * @author wanyujun
     */
    public function getDomainPort($body,$msgType)
    {
        $count = strlen($body);
        if ($count > 2) {
            $cfgLen = hexdec(substr($body,0,2)) * 2;
            $cfgDomain = hex2ascii(str_split(substr($body,2,$cfgLen), 2));
            $cfgPort = hexdec(substr($body,$cfgLen+2,4));

            $dataLen = hexdec(substr($body,$cfgLen+2+4,2)) * 2;
            $dataDomain = hex2ascii(str_split(substr($body,$cfgLen+2+4+2,$dataLen), 2));
            $dataPort = hexdec(substr($body,-4,4));

            $data = array("dataLen" => $dataLen / 2, "dataDomain" => $dataDomain, "dataPort" => $dataPort, "cfgLen" => $cfgLen / 2, "cfgDomain" => $cfgDomain,"cfgPort"=>$cfgPort);
            $relArr = array("type" => $msgType, "data" => $data);
            return $relArr;
        } else {
            return $this->handStatus($body, $msgType);
        }
    }

    /**
     * 设置服务器的IP和端口
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 2007
     * @author wanyujun
     */
    public function setIpPort($body,$msgType)
    {
        return $this->handStatus($body,$msgType);
    }

    /**
     * 获取服务器的IP和端口
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 2008
     * @author wanyujun
     */
    public function getIpPort($body,$msgType)
    {
        $count=strlen($body);
        if($count>2){
            $cfgIP = long2ip(hexdec(substr($body, 0, 8)));
            $cfgPort= hexdec(substr($body, 8, 4));

            $dataIP=long2ip(hexdec(substr($body, 12, 8)));
            $dataPort=hexdec(substr($body,20,4));

            $data = array(
                'dataIP' => $dataIP,
                'dataPort' => $dataPort,
                'cfgIP' => $cfgIP,
                'cfgPort' => $cfgPort,
            );
            $relArr = array("type" => $msgType, "data" => $data);
            return $relArr;
        }
        else{
            return $this->handStatus($body,$msgType);
        }

    }

    /**
     * 设置服务器地址策略
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 2009
     * @author wanyujun
     */
    public function setServerPolicy($body,$msgType)
    {
        return $this->handStatus($body,$msgType);
    }

    /**
     * 获取服务器地址策略
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 200A
     * @author wanyujun
     */
    public function getServerPolicy($body,$msgType)
    {
        if(strlen($body) > 2){
            $dataPolicy = long2ip(hexdec(substr($body, 0, 2)));
            $cfgPolicy = long2ip(hexdec(substr($body, 2, 2)));
            $relArr = array("type" => $msgType, "data" => array('cfg' => $cfgPolicy, 'data' => $dataPolicy));
            return $relArr;
        }
        else{
            return $this->handStatus($body,$msgType);
        }
    }

    /**
     * 设置设备下线时间
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 200B
     * @author wanyujun
     */
    public function setDevOfflineTime($body,$msgType)
    {
        return $this->handStatus($body,$msgType);
    }

    /**
     * 设置工作模式
     * @param string $body 消息内容
     * @param string $msgType 消息类型
     * @return array
     * @author wanyujun
     */
    public function setWorkMode($body, $msgType)
    {
        $data = array(
            'mode' => hexdec($body),
        );
        $relArr = array("type" => $msgType, "data" => $data);
        return $relArr;
    }

    /**
     * 设置过滤策略
     * @param string $body 消息内容
     * @param string $msgType 消息类型
     * @return array
     * @author wanyujun
     */
    public function setFitler($body, $msgType)
    {
        $data = array('status' => hexdec($body));
        $relArr = array("type" => $msgType, "data" => $data);
        return $relArr;
    }

    /**
     * 获取过滤策略
     * @param string $body 消息内容
     * @param string $msgType 消息类型
     * @return array
     * @author wanyujun
     */
    public function getFitler($body, $msgType)
    {
        $data = str_split($body, 12);
        $relArr = array("type" => $msgType, "data" => $data);
        return $relArr;
    }

    /**
     * 设备固件升级
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 1013
     * @author wanyujun
     */
    public function upgradeResult($body, $msgType)
    {
        return array('type' => $msgType, 'data' => array('status' => hexdec($body)));
    }

    /**
     * 设备固件升级
     * @param $body 消息体
     * @param $msgType 消息类型
     * @return 解析完成的协议
     * @code 1011
     * @author wanyujun
     */
    public function setFirmwareData($body, $msgType){
        $deviceID = hex2ascii(str_split(substr($body, 0, 36), 2));
        $firmware = hex2ascii(str_split(substr($body, 36, 60), 2));
        $packlength = hexdec(substr($body, 96, 4));
        $status = hexdec(substr($body, 100, 2));
        $data = array(
            'type' => $msgType,
            'data' => array(
                'deviceID' => $deviceID,
                'frimware' => $firmware,
                'packlength' => $packlength,
                'status' => $status,
            ),
        );
        return $data;
    }

    /**
     * 获取指定序号的升级包数据
     * @param $body
     * @param $msgType
     * @code 1012
     * @return array
     */
    public function getFirmwareData($body, $msgType) {
        $pack = hexdec($body);
        return array('type' => $msgType, 'data' => array('pack' => $pack));
    }

    /**
     * 设置上报数据间隔
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 4000
     * @author qmx
     */
    public function setDataSpace($body,$msgType)
    {
        $status = hexdec($body);
        return array("type" => $msgType, 'data' => $status);
    }

    /**
     * 获取上报数据间隔
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 4001
     * @author qmx
     */
    public function getDataSpace($body,$msgType){
        $ap = hexdec(substr($body,0,4));
        $sta = hexdec(substr($body,4,4));
        $rf = hexdec(substr($body,8,4));
        $spaces = array(
            "ap" => $ap,
            "sta" => $sta,
            "rf" => $rf
        );
        return array("type" => $msgType, "data" => $spaces);
    }

    /**
     * 设置数据上传方式
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 4002
     * @author qmx
     */
    public function setDataUploadWay($body,$msgType){
        return $this->handStatus($body,$msgType);
    }

    /**
     * 获取数据上传方式
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 4003
     * @author qmx
     */
    public function getDataUploadWay($body,$msgType){
        return $this->handStatus($body,$msgType);
    }

    /**
     * 获取上报错误消息
     * @param $body
     * @param $msgType
     * @return array
     */
    public function getUpError($body, $msgType) {
        $errCode = str_split($body, 2);
        return array('type' => $msgType, 'data' => array(
            'cfg' => $this->parseErrInfo($errCode[0], 5, self::$errMsg['cfg']),
            'data' => $this->parseErrInfo($errCode[1], 6, self::$errMsg['data']),
            'device' => $this->parseErrInfo($errCode[2], 6, self::$errMsg['device']),
            'temperature' => $this->parseErrInfo($errCode[3], 4, self::$errMsg['temperature']),
            'power' => $this->parseErrInfo($errCode[4], 2, self::$errMsg['power']),
        ));
    }



    /**
     * 获取设备信息
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 6000
     * @author qmx
     */
    public function getDevice($body,$msgType) {
        $deviceID = hex2ascii(str_split(substr($body,0,36 ), 2));
        $softwareVer = hexdec(substr($body,36,2));
        $hardwareVer = hexdec(substr($body,38,2));
        $protocolVer = hexdec(substr($body,40,2));
        $workmode = hexdec(substr($body,42,2));
        $array = array("deviceID" => $deviceID, "softwareVer" => $softwareVer, "hardwareVer" => $hardwareVer, "protocolVer" => $protocolVer, 'workmode' => $workmode);
        $relArr = array("type" => $msgType, "data" => $array);
        return $relArr;
    }

    /**
     * 获取设备状态
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 6001
     * @author qmx
     */
    public function getDeviceStatus($body, $msgType)
    {

        $net = hexdec(substr($body,0,2));
        $temp = hexdec(substr($body,2,2));
        $fan = hexdec(substr($body,4,2));
        $battery = bitout(hexdec(substr($body,6,2)));
        $array = array(
            "net" => self::$devStatus['net'][$net],
            "temp" => isset(self::$devStatus['temp'][$temp]) ? self::$devStatus['temp'][$temp] : $temp - 127,
            "fan" => self::$devStatus['fan'][$fan],
            "battery" => array(
                self::$devStatus['battery']['status'][$battery[1]],
                self::$devStatus['battery']['quantity'][$battery[0]],
            ),
        );
        return array("type" => $msgType, "data"=>$array);
    }

    /**
     * 获取设备位置信息
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 6002
     * @author qmx
     */
    public function getDeviceLocal($body, $msgType){
        if(strlen($body) == 2){  //数据格式1
            return $this->handStatus($body,$msgType);
            //数据格式2
        } else {
            $lng = hexdec(substr($body,0,22));
            $lat = hexdec(substr($body,22,20));
            $altitude = hexdec(substr($body,42,16));
            $array = array("longitude" => $lng,"dimension" => $lat,"altitude" => $altitude);
            $relArr=array("type" => $msgType, "data" => $array);
            return $relArr;
        }

    }

    /**
     * 获取设备故障信息
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 6003
     * @author qmx
     */
    public function getDeviceFault($body,$msgType){
        $configServer=substr($body,0,2);
        $datServer=substr($body,2,2);
        $deviceSelf =substr($body,4,2);
        $temperature=substr($body,6,2);
        $powerSupply=substr($body,8,2);
        $array=array("configServer"=>$configServer,"datServer"=>$datServer,"deviceSelf"=>$deviceSelf,"temperature"=>$temperature,"powerSupply"=>$powerSupply);

        $relArr=array("type"=>$msgType,"data"=>$array);
        return $relArr;

    }

    /**
     * 设备故障信息上报
     * @param string $body 消息体
     * @return array $shuju 解析成功的数组数据
     * @code 6103
     * @author qmx
     */
    function getDeviceFaultReport($body,$msgType){
        return $this->handStatus($body,$msgType);
    }

    /**
     *  上传AP数据
     *  @param string $body 消息体
     *  @return array $shuju 解析成功的数组数据
     *  @code A000
     *  @author qmx
     */
    function uploadApData($body,$msgType){

        $row=$body;

            $frmtype=substr($row,0,2);
            $bssid = substr($row,4,12);

            $ssid_len = hexdec(substr($row,14,2));
            $ssid = hex2ascii(str_split(substr($row,16, ($ssid_len * 2)), 2));

            $channel = hexdec(substr($row,(16 + ($ssid_len * 2)),2));
            $rssi = rssi(hexdec(substr($row,(18 + ($ssid_len * 2)),2)));
            $is_hidden = substr($row,(20 + ($ssid_len * 2)),2);
        $array = array("frametype"=>$frmtype,"mac"=>$bssid,"ssid_len"=>$ssid_len,"ssid"=>$ssid,"channel"=>$channel,"rssi"=>$rssi,"is_hidden"=>$is_hidden);

        $relArr=array("type"=>$msgType,"data"=>$array,"datastr"=>$row);
        return $relArr;
    }

    /**
     * 上传STA数据
     *  @param string $body 消息体
     *  @return array $shuju 解析成功的数组数据
     *  @code A001
     *  @author qmx
     */
    public function uploadStaData($body,$msgType){
        //处理数据

        $row=$body;

        $frmtype=substr($row,0,2);
        $mac=substr($row,2,12);
        $rssi=rssi(hexdec(substr($row,14,2)));
        $Has_ap =substr($row,16,2);
        $ApMac=$Has_ap =='00'?"00":substr($row,18,12);
        $array=array("frametype"=>$frmtype,"mac"=>$mac,"rssi"=>$rssi,"Has_ap"=>$Has_ap,"ApMac"=>$ApMac);

        $relArr=array("type"=>$msgType,"data"=>$array,"datastr"=>$row);
        return $relArr;
    }

    /**
     * 上传RF数据
     *  @param string $body 消息体
     *  @return array $shuju 解析成功的数组数据
     *  @code A002
     *  @author qmx
     */
    public function uploadRfData($body,$msgType){
        $data=$this->getSplitMsgLine($body,26);
        $shuju=array();
        for($i=0;$i<count($data);$i++)
        {
            $deviceID= substr($data[$i],0,16);
            $battery=substr($data[$i],16,4);
            $sendRssi=  rssi(hexdec(substr($data[$i],20,2)));
            $rssi= rssi(hexdec(substr($data[$i],22,2)));
            $urgentStatus=(int)substr($data[$i],24,2);// ? '紧急' : '正常';
            $array=array("type"=>$msgType,"mac"=>$deviceID,"battery"=>$battery,"sendRssi"=>$sendRssi, "rssi"=>$rssi,"urgentStatus"=>$urgentStatus);
            $shuju[]= array("type"=>$msgType,"data"=>$array,"datastr"=>$body);;
        }
        return $shuju;
    }


    //十六进制发送格式
    function pushhex($string) {
        $checksum = 0x0;
        $cmdStrArr = explode(' ',$string);
        for($i=0;$i<count($cmdStrArr);$i++){
            $cmdHex[] = chr(hexdec($cmdStrArr[$i]));
        }

        $cmdHex[] = chr($checksum);
        $cmdHex=implode($cmdHex);
        return $cmdHex;
    }

    //=============================================公共方法=============================================

    /**
     * 调用相应解析协议的方法
     * @param $body 协议消息
     * @param $body 协议类型
     */
    public function getdata($body,$msgType)
    {
        //获取消息类型再调用map映射的方法
        //  $msgType = substr($msg,0,4);
        if(!isset($this->_map[$msgType])) return false;

        $method = $this->_map[$msgType];
        return $this->$method($body,$msgType);
    }

    /**
     * 设置状态解析协议的公共方法
     * @param $body  消息主体
     * @param $msgType 消息类型
     * @return $relArr  返回解析成功的协议
     */
    public function handStatus($body,$msgType)
    {
        $relArr = array("type"=>$msgType,"data"=>hexdec($body));
        return $relArr;
    }

    /**
     * @param array $msg 消息主体
     * @param bool $isbody 是否存在消息主体
     * @param bool $ishash 是否有md5
     * @return mixed
     */
    public function parse($msg, $isbody = true, $ishash = true)
    {
        $data['type'] = substr($msg,0, 4);
        $data['length'] = substr($msg,4, 4);
        if ($isbody && $ishash) {
            $data['body'] = substr($msg, 8, -16);
        } else if ($isbody && !$ishash) {
            $data['body'] = substr($msg, 8);
        }
        if ($ishash) {
            $data['hash'] = substr($msg, -16);
        }
        return $data;
    }

    /**
     * 按照长度分割消息
     * @param  string $body 消息内容
     * @param  int $type 单条消息长度
     * @return array
     */
    public function getSplitMsgLine($body,$type){
        $data = str_split($body, $type);
        return $data;
    }

    /**
     * 根据字符串得到正确格式的数据
     * @param  [type] $msgbody [description]
     * @param  [type] $type    [description]
     * @param  [type] $detail  [description]
     * @return [type]          [description]
     */
    private function getMsg($msgbody,$type,$detail)
    {

        $count=strlen($msgbody)/intval($type); //消息的条数
        for($i=0;$i<$count;$i=$i+$type)
        {

            $str=substr($msgbody,$i*$type,$type);
            $data[]=$str;
        }
        //data为每条数据的包装结果
        for($i=0;$i<count($data);$i++){
            $shuju=$data[$i];
            $str="";
            for($j=0;$j<count($detail);$j++){
                $num=$detail[$j];

                if($j==0){
                    $x=0;
                    $str.=substr($shuju,$x,$num);
                    //echo "str:".$str;
                }else{

                    $x=$x+$detail[$j-1];

                    $str=$str." ".substr($shuju,$x,$num);

                }

            }

            $array[]=$str;
        }
        return $array;
    }

    //消息体分割(2位一分割)
    private function splitMsgBody($str)
    {
        $data=array();
        for ($i = 0; $i < strlen($str); $i = $i+2)
        {
            $data[] = $str[$i] . $str[$i+1];
        }
        return $data;
    }

    /**
     * 解析设备故障信息
     * @param $num
     * @param $bit
     * @param $errMsg
     * @return array
     */
    public function parseErrInfo($num, $bit, $errMsg) {
        $num = hexdec($num);
        $bin = array_reverse(str_split(sprintf("%0{$bit}b", $num), 1));
        $data = array();
        foreach ($bin as $key => $r) {
            if ($r == "1") $data[] = $errMsg[$key];
        }
        return $data;
    }
}

?>